package com.ehome.check.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.ehome.exception.BizException;
import io.swagger.annotations.ApiModelProperty;
import lombok.experimental.UtilityClass;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Excel工具类再次封装
 *
 * @author lucas
 * @since 2020/12/23 14:48
 */
@UtilityClass
public class ExcelUtils extends ExcelUtil {

    private static final Integer MAX_LIMIT_ROWS = 100_000;

    /**
     * 写入内容到默认sheet并关闭流
     *
     * @param excelWriter excelWriter
     * @param fileName    文件名
     * @param coll        需写入的内容
     * @throws IOException 获取Response的OutPutStream失败
     */
    public <T> void write(ExcelWriter excelWriter, String fileName, Collection<T> coll) throws IOException {
        if (CollUtil.isEmpty(coll)) {
            return;
        }
        if (coll.size() > MAX_LIMIT_ROWS) {
            throw new BizException(201, "导出数据超限!!!");
        }
        write(excelWriter, fileName, coll, getResponse());
    }

    /**
     * 写入内容到默认sheet并关闭流
     *
     * @param excelWriter excelWriter
     * @param fileName    文件名
     * @param coll        需写入的内容
     * @param response    response
     * @throws IOException 获取Response的OutPutStream失败
     */
    public <T> void write(ExcelWriter excelWriter, String fileName, Collection<T> coll, HttpServletResponse response) throws IOException {
        if (CollUtil.isEmpty(coll)) {
            return;
        }
        if (coll.size() > MAX_LIMIT_ROWS) {
            throw new BizException(201, "导出数据超限!!!");
        }
        excelWriter.write(coll, true);
        write(excelWriter, fileName, response);
    }

    /**
     * 将excelWriter中的内容输出并关闭流
     *
     * @param excelWriter excelWriter
     * @param fileName    文件名
     * @throws IOException 获取Response的OutPutStream失败
     */
    public void write(ExcelWriter excelWriter, String fileName) throws IOException {
        write(excelWriter, fileName, getResponse());
    }

    /**
     * 将excelWriter中的内容输出并关闭流
     *
     * @param excelWriter excelWriter
     * @param fileName    文件名
     * @param response    response
     * @throws IOException 获取Response的OutPutStream失败
     */
    public void write(ExcelWriter excelWriter, String fileName, HttpServletResponse response) throws IOException {
        setHeader(excelWriter, fileName, response);
        close(excelWriter, response);
    }

    /**
     * 设置下载文件时的请求头
     *
     * @param excelWriter excelWriter
     * @param fileName    文件名
     */
    public void setHeader(ExcelWriter excelWriter, String fileName) {
        setHeader(excelWriter, fileName, getResponse());
    }

    /**
     * 设置下载文件时的请求头
     *
     * @param excelWriter excelWriter
     * @param fileName    文件名
     * @param response    response
     */
    public void setHeader(ExcelWriter excelWriter, String fileName, HttpServletResponse response) {
        if (excelWriter.isXlsx()) {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        } else {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
        }

        response.setHeader("Content-Disposition", getDisposition(excelWriter, fileName, CharsetUtil.CHARSET_UTF_8));
    }

    /**
     * 将ExcelWriter中的内容刷出到输出流，并关闭流
     *
     * @param excelWriter excelWriter
     * @throws IOException 获取Response的OutPutStream失败
     */
    public void close(ExcelWriter excelWriter) throws IOException {
        close(excelWriter, getResponse());
    }

    /**
     * 将ExcelWriter中的内容刷出到输出流，并关闭流
     *
     * @param excelWriter excelWriter
     * @param response    response
     * @throws IOException 获取Response的OutPutStream失败
     */
    public void close(ExcelWriter excelWriter, HttpServletResponse response) throws IOException {
        ServletOutputStream out = response.getOutputStream();
        excelWriter.flush(out, true);
        excelWriter.close();
        IoUtil.close(out);
    }

    public <T> Map<String, String> getHeaderAliasMap(Class<T> tClass) {
        return Arrays.stream(tClass.getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(ApiModelProperty.class))
                .collect(Collectors.toMap(Field::getName, field -> field.getAnnotation(ApiModelProperty.class).value(),
                        (o, o2) -> o, LinkedHashMap::new));
    }

    /**
     * 获取Content-Disposition头对应的值，可以通过调用以下方法快速设置下载Excel的头信息：
     *
     * <pre>
     * response.setHeader("Content-Disposition", excelWriter.getDisposition("test.xlsx", CharsetUtil.CHARSET_UTF_8));
     * </pre>
     *
     * @param fileName 文件名，如果文件名没有扩展名，会自动按照生成Excel类型补齐扩展名，如果提供空，使用随机UUID
     * @param charset  编码，null则使用默认UTF-8编码
     * @return Content-Disposition值
     */
    public String getDisposition(ExcelWriter excelWriter, String fileName, Charset charset) {
        if (null == charset) {
            charset = CharsetUtil.CHARSET_UTF_8;
        }

        if (StrUtil.isBlank(fileName)) {
            // 未提供文件名使用随机UUID作为文件名
            fileName = IdUtil.fastSimpleUUID();
        }

        fileName = StrUtil.addSuffixIfNot(URLUtil.encodeAll(fileName, charset), excelWriter.isXlsx() ? ".xlsx" : ".xls");
        return StrUtil.format("attachment;filename={};filename*={}''{}", fileName, charset.name(), fileName);
    }

    public HttpServletResponse getResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }


    /**
     * 写入内容并关闭流
     *
     * @param response    无用，兼容旧代码
     * @param coll        写入内容
     * @param fileName    文件名
     * @param excelWriter ExcelWriter
     * @throws IOException IO异常
     */
    @Deprecated
    public <T> void closeExcelStream(HttpServletResponse response, Collection<T> coll, String fileName, ExcelWriter excelWriter) throws IOException {
        excelWriter.write(coll, true);
        write(excelWriter, fileName);
    }
}